package com.hp.interceptor;

import com.hp.annocatation.RedisLock;
import com.hp.utils.SpringElUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * redis锁拦截器 AOP
 *
 * @author hp
 */
@Slf4j
@Aspect
@Component
//@ConditionalOnBean(RedissonClient.class)
public class RedisLockInterceptor {

    @Resource
    private RedissonClient redissonClient;

    @Around("@annotation(com.hp.annocatation.RedisLock)")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {

        log.info("线程{}，进入切面", Thread.currentThread().getName());

        Method method = ((MethodSignature) pjp.getSignature()).getMethod();
        RedisLock redisLock = AnnotationUtils.getAnnotation(method, RedisLock.class);
        assert redisLock != null;

        String key = redisLock.key();
        Object target = pjp.getTarget();
        Object[] args = pjp.getArgs();
        // 支持spring El表达式
        key = SpringElUtil.parse(key, target, method, args);

        // 获取锁
        RLock lock = this.getLock(key, redisLock);
        boolean isLocked = lock.tryLock(redisLock.leaseTime(), redisLock.unit());

        if (!isLocked) {
            log.error("无法获取锁: {}", key);
            throw new RuntimeException("无法获取锁: " + key);
        }
        log.info("加锁成通过");

        try {
            log.info("执行业务逻辑");
            return pjp.proceed();
        } catch (Exception e) {
            log.error("RedisLockInterceptor around error", e);
            throw e;
        } finally {
            //释放锁
            log.info("释放锁");
            lock.unlock();
        }


    }

    private RLock getLock(String key, RedisLock redisLock) {

        return switch (redisLock.lockType()) {
            case REENTRANT_LOCK -> redissonClient.getLock(key);
            case FAIR_LOCK -> redissonClient.getFairLock(key);
            case READ_LOCK -> redissonClient.getReadWriteLock(key).readLock();
            case WRITE_LOCK -> redissonClient.getReadWriteLock(key).writeLock();
        };
    }

}
